package band.wukong.practice.webservice.kh.sample05.nongen;

import band.wukong.practice.webservice.kh.sample05.Artist;
import band.wukong.practice.webservice.util.raw.JAXBUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.annotations.Test;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;

import javax.xml.namespace.QName;
import javax.xml.soap.*;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.stream.StreamSource;
import javax.xml.ws.Dispatch;
import javax.xml.ws.Service;
import javax.xml.ws.soap.SOAPFaultException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
import java.io.StringReader;
import java.net.MalformedURLException;
import java.net.URL;

/**
 * @author wukong(wukonggg@139.com)
 */
public class JukeboxClientBySAAJ {

    private final Logger logger = LoggerFactory.getLogger(JukeboxClientBySAAJ.class);

    /**
     * 演示如何创建一个SOAP消息
     *
     * @throws Exception
     */
    @Test
    public void createSoapMessage() throws Exception {
        MessageFactory factory = MessageFactory.newInstance();
        SOAPMessage message = factory.createMessage();

        SOAPPart part = message.getSOAPPart();
        SOAPEnvelope envelope = part.getEnvelope();
        SOAPBody body = envelope.getBody();

        //根据QName创建相应节点（QName是一个带有命名空间的节点）
        QName qName = new QName(JukeboxConst.SERVICE_NS, "randomArtist", "nn");
//      Wrong way:
//        body.addBodyElement(qName).setValue("<genre>" + String.valueOf(MusicConst.GENRE_BLUES) + "</genre>");

        SOAPBodyElement ele = body.addBodyElement(qName);
        ele.addChildElement("genre").setValue(String.valueOf(MusicConst.GENRE_BLUES));

        logger.info("Response message:");
        message.writeTo(System.out);
        System.out.println();
    }

    /**
     * 发送SOAP消息，通过Mode.Message
     *
     * @throws Exception
     */
    @Test
    public void testRandomArtist() throws Exception {

        // 1.1、创建SOAPMessage
        SOAPMessage reqMessage = MessageFactory.newInstance().createMessage();
        SOAPEnvelope envlope = reqMessage.getSOAPPart().getEnvelope();
        SOAPBody body = envlope.getBody();

        // 1.2、创建QName来指定消息中传递数据. <nn:randomArtist xmlns="xx"/>
        QName elementQName = new QName(JukeboxConst.SERVICE_NS, "randomArtist", "nn");
        SOAPBodyElement ele = body.addBodyElement(elementQName);
        ele.addChildElement("genre").setValue(String.valueOf(MusicConst.GENRE_POP));

        // 1.3 打印
        logger.info("Request message:");
        reqMessage.writeTo(System.out);
        logger.info("invoking...");


        // 2、创建服务
        URL url = new URL(JukeboxConst.SERVICE_WSDL);
        QName serviceQName = new QName(JukeboxConst.SERVICE_NS, JukeboxConst.SERVICE_NAME);
        Service service = Service.create(url, serviceQName);

        // 3、创建Dispatch
        Dispatch<SOAPMessage> dispatch = service.createDispatch(
            new QName(JukeboxConst.SERVICE_NS, "JukeboxServiceImplPort"),
            SOAPMessage.class,
            Service.Mode.MESSAGE);

        //3、通过Dispatch传递消息
        SOAPMessage respMessage = dispatch.invoke(reqMessage);

        logger.info("Response message:");
        respMessage.writeTo(System.out);
        System.out.println();

        // 4、将响应的消息转换为DOM对象
        Document doc = respMessage.getSOAPPart().getEnvelope().getBody().extractContentAsDocument();
        String retValue = doc.getElementsByTagName("result-randomArtist").item(0).getTextContent();
        logger.info("result-randomArtist = " + retValue);
    }

    /**
     * 发送SOAP消息，通过Mode.Payload
     *
     * @throws Exception
     */
    @Test
    public void testAddArtist() throws Exception {
        // 1、根据PO创建相应的xml。封装part addArtist
        final String payload = "<id>666</id><name>Jimmy Hendrix</name>";
        final String part = "<nn:addArtist xmlns:nn=\"" + JukeboxConst.SERVICE_NS + "\">" + payload + "</nn:addArtist>";
        logger.info("Request soap part = " + part);

        // 2、创建服务
        Service service = createService(false);

        // 3、创建Dispatch（通过Source）
        Dispatch<Source> dispatch = service.createDispatch(
            new QName(JukeboxConst.SERVICE_NS, "JukeboxServiceImplPort"),
            Source.class,
            Service.Mode.PAYLOAD);


        // 4、通过dispatch传递payload
        StreamSource streamSource = new StreamSource(new StringReader(part));
        Source response = dispatch.invoke(streamSource);

        // 5、将source转化为DOM来操作
        Transformer trans = TransformerFactory.newInstance().newTransformer();
        DOMResult domResult = new DOMResult();
        trans.transform(response, domResult);

        // 6、通过xpath处理信息
        XPath xPath = XPathFactory.newInstance().newXPath();
        NodeList nodeList = (NodeList) xPath
            .evaluate("//artist", domResult.getNode(), XPathConstants.NODESET);
        Artist artist = JAXBUtil.unmarshall(Artist.class, nodeList.item(0));

        logger.info("Response objcet = " + artist);
        // TODO 不知道为啥，主工程中的unmarshall是正常的，但是放到soapclient工程里就报错了。
    }

    private Service createService(boolean debug) throws MalformedURLException {
        URL url = null;
        if (debug) {
            url = new URL(JukeboxConst.SERVICE_WSDL_DEBUG);
        } else {
            url = new URL(JukeboxConst.SERVICE_WSDL);
        }
        QName serviceQName = new QName(JukeboxConst.SERVICE_NS, JukeboxConst.SERVICE_NAME);
        return Service.create(url, serviceQName);
    }

    /**
     * 发送SOAP消息，通过Mode.Message
     *
     * @throws Exception
     */
    @Test
    public void testListArtists() throws Exception {
        // 1.1、创建SOAPMessage
        SOAPMessage reqMessage = MessageFactory.newInstance().createMessage();
        SOAPEnvelope reqEnvlope = reqMessage.getSOAPPart().getEnvelope();
        SOAPBody reqSoapBody = reqEnvlope.getBody();

        // 1.2、创建QName来指定消息中传递数据. <nn:randomArtist xmlns="xx"/>
        QName elementQName = new QName(JukeboxConst.SERVICE_NS, "listArtists", "nn");
        reqSoapBody.addBodyElement(elementQName);

        // 1.3、打印
        logger.info("Request message = ");
        reqMessage.writeTo(System.out);
        System.out.println("\n");

        // 2.1、创建服务
        URL url = new URL(JukeboxConst.SERVICE_WSDL);
        QName serviceQName = new QName(JukeboxConst.SERVICE_NS, JukeboxConst.SERVICE_NAME);
        Service service = Service.create(url, serviceQName);

        // 2.2、创建Dispatch
        Dispatch<SOAPMessage> dispatch = service.createDispatch(
            new QName(JukeboxConst.SERVICE_NS, "JukeboxServiceImplPort"),
            SOAPMessage.class,
            Service.Mode.MESSAGE);

        // 2.3、通过Dispatch传递消息
        logger.info("invoking...");
        SOAPMessage response = dispatch.invoke(reqMessage);
        logger.info("Response message = ");
        response.writeTo(System.out);
        System.out.println();

        // 将响应的消息转换为DOM对象
        Document doc = response.getSOAPPart().getEnvelope().getBody().extractContentAsDocument();
        NodeList nodeList = doc.getElementsByTagName("artist");
        for (int i = 0; i < nodeList.getLength(); i++) {
            // String s = nodeList.item(i).getTextContent();
            org.w3c.dom.Node node = nodeList.item(i);
            Artist artist = JAXBUtil.unmarshall(Artist.class, node);
            logger.info("artist = " + artist);
        }
        // TODO 不知道为啥，主工程中的unmarshall是正常的，但是放到soapclient工程里就报错了。
    }

    /**
     * header 显式Header的处理方式。message中多了
     *
     * @throws Exception
     */
    @Test
    public void testUpdateArtistPrimally() throws Exception {
        // 1.1、创建SOAPMessage
        SOAPMessage reqMessage = MessageFactory.newInstance().createMessage();
        SOAPEnvelope reqEnvlope = reqMessage.getSOAPPart().getEnvelope();
        SOAPBody reqEnvlopeBody = reqEnvlope.getBody();

        // 1.2、处理header
        SOAPHeader header = reqEnvlope.getHeader();
        if (null == header) header = reqEnvlope.addHeader();
        QName headerQName = new QName(JukeboxConst.SERVICE_NS, "authInfo", "nn");
        header.addHeaderElement(headerQName).setValue("信息安全服务");

        // 1.3、创建QName来指定消息中传递数据. <nn:randomArtist xmlns="xx"/>
        QName elementQName = new QName(JukeboxConst.SERVICE_NS, "updateArtist", "nn");
        SOAPBodyElement ele = reqEnvlopeBody.addBodyElement(elementQName);
        SOAPElement a = ele.addChildElement("artist");
        a.addChildElement("id").setValue("666");
        a.addChildElement("name").setValue("666");

        // 1.4、打印
        logger.info("Request message = ");
        reqMessage.writeTo(System.out);
        System.out.println();

        // 2、创建服务
        URL url = new URL(JukeboxConst.SERVICE_WSDL);
        QName serviceQName = new QName(JukeboxConst.SERVICE_NS, JukeboxConst.SERVICE_NAME);
        Service service = Service.create(url, serviceQName);

        // 3、创建Dispatch
        Dispatch<SOAPMessage> dispatch = service.createDispatch(
            new QName(JukeboxConst.SERVICE_NS, "JukeboxServiceImplPort"),
            SOAPMessage.class, Service.Mode.MESSAGE);

        //4、通过Dispatch传递消息
        logger.info("invoking...");
        SOAPMessage response = dispatch.invoke(reqMessage);
        logger.info("Response message = ");
        response.writeTo(System.out);
        System.out.println();

    }

    /**
     *
     * @throws Exception
     */
    @Test
    public void testRemoveArtist() throws Exception {
        // 1.1、创建SOAPMessage
        SOAPMessage reqMessage = MessageFactory.newInstance().createMessage();

        // 1.2、创建QName来指定消息中传递数据. <nn:randomArtist xmlns="xx"/>
        SOAPEnvelope reqEnvlope = reqMessage.getSOAPPart().getEnvelope();
        SOAPBody reqEnvlopeBody = reqEnvlope.getBody();
        QName elementQName = new QName(JukeboxConst.SERVICE_NS, "removeArtist", "nn");
        SOAPBodyElement ele = reqEnvlopeBody.addBodyElement(elementQName);
        SOAPElement a = ele.addChildElement("artist");
        a.addChildElement("id").setValue("666");
        a.addChildElement("name").setValue("666");

        // 1.3、打印
        logger.info("Request Message = ");
        reqMessage.writeTo(System.out);
        System.out.println();

        // 2、创建服务
        URL url = new URL(JukeboxConst.SERVICE_WSDL_DEBUG);
        QName serviceQName = new QName(JukeboxConst.SERVICE_NS, JukeboxConst.SERVICE_NAME);
        Service service = Service.create(url, serviceQName);

        // 3、创建Dispatch
        Dispatch<SOAPMessage> dispatch = service.createDispatch(
            new QName(JukeboxConst.SERVICE_NS, "JukeboxServiceImplPort"),
            SOAPMessage.class,
            Service.Mode.MESSAGE);


        //4、通过Dispatch传递消息
        logger.info("invoking...");

        try {

            SOAPMessage response = dispatch.invoke(reqMessage);
            logger.info("Response message = ");
            response.writeTo(System.out);System.out.println();
        } catch (SOAPFaultException e) {
            logger.error(e.getMessage());
            e.printStackTrace();
            logger.error("往上看！！！！！捕捉到异常！！！！！");
        }
    }

}
